/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.aternity.geolocations.draw;

import com.aternity.common.geolocation.FormattingUtils;
import com.aternity.common.geolocation.GeomUtils;
import com.aternity.common.geolocation.PolygonFormatter;
import com.aternity.common.geom.Polygon2D;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 *
 * @author danielo
 */
public class DrawingPanel extends JPanel {

    private static final Logger logger = LoggerFactory.getLogger("DrawingPanel");

    private boolean showCoordinates = false;
    private boolean showReducedPolygons = true;
    private boolean autoScale = true;

    private List<Polygon> transformedPolygons = new ArrayList<>();
    private List<Polygon> transformedPolygonsReduced = new ArrayList<>();
    private List<Rectangle2D> transformedRectangles = new ArrayList<>();

    private List<Polygon2D> originalPolygons;
    private List<Polygon2D> originalPolygonsReduced;
    private List<Rectangle2D> originalRectangles;

    private final PolygonFormatter polygonFormatter = new PolygonFormatter();

    private double scalingFactor;
    private double translateX;
    private double translateY;
    private double reducedPolygonsTolerance = 0.5;

    private final List<Point2D> points = new ArrayList<>();
    private final List<Point2D> transformedPoints = new ArrayList<>();

    public DrawingPanel() {
        this.setBackground(Color.white);
        this.setLayout(new BorderLayout());
    }

    public void setShowCoordinates(boolean showCoordinates) {
        this.showCoordinates = showCoordinates;
        repaint();
    }

    public void setShowReducedPolygons(boolean showReducedPolygons) {
        this.showReducedPolygons = showReducedPolygons;
    }

    public void setReducedPolygonsTolerance(double reducedPolygonsTolerance) {
        this.reducedPolygonsTolerance = reducedPolygonsTolerance;
    }

    public boolean isShowCoordinates() {
        return showCoordinates;
    }

    public boolean isShowReducedPolygons() {
        return showReducedPolygons;
    }

    public double getReducedPolygonsTolerance() {
        return reducedPolygonsTolerance;
    }

    public void setAutoScale(boolean autoScale) {
        this.autoScale = autoScale;
    }

    public void setTranslateX(double translateX) {
        this.translateX = translateX;
    }

    public void setTranslateY(double translateY) {
        this.translateY = translateY;
    }

    public void setScalingFactor(double scalingFactor) {
        this.scalingFactor = scalingFactor;
    }

    public double getScalingFactor() {
        return scalingFactor;
    }

    public double getTranslateX() {
        return translateX;
    }

    public double getTranslateY() {
        return translateY;
    }

    public void setPolygonData(String polyData) {
        originalPolygons = polygonFormatter.parse(polyData);
        setPolygonData(originalPolygons);
    }

    public void setPolygonData(Collection<Polygon2D> polygons) {
        originalPolygons = new ArrayList<>(polygons);
        refreshReducedPolygonsImpl(false);
        originalPolygonsReduced = GeomUtils.reduce(originalPolygons, reducedPolygonsTolerance);
        if (autoScale) {
            System.out.println("Auto scaling is on");
            autoScaleAndTranslate();
        } else {
            refreshPolygon();
        }
        this.repaint();
    }

    public void refreshReducedPolygons() {
        refreshReducedPolygonsImpl(true);
    }
    
    private void refreshReducedPolygonsImpl(boolean repaint){
        System.out.println("Refreshing reduced polygons");
        originalPolygonsReduced = GeomUtils.reduce(originalPolygons, reducedPolygonsTolerance);
        transformedPolygonsReduced = getScaledAwtPolygon(originalPolygonsReduced);
        if(repaint){
            repaint();
        }
    }
    
    private void refreshPolygon() {
        if (originalPolygons != null) {
            transformedPolygons = getScaledAwtPolygon(originalPolygons);
            transformedPolygonsReduced = getScaledAwtPolygon(originalPolygonsReduced);
        }
    }

    private List<Polygon> getScaledAwtPolygon(List<Polygon2D> polygonsIn) {
        List<Polygon> result = new ArrayList<>();
        for (Polygon2D polygonIn : polygonsIn) {
            Polygon polygonOut = new Polygon();
            Point2D[] pts = polygonIn.getPoints();
            for (Point2D point : pts) {
                int x = (int) (point.getX() * scalingFactor + translateX);
                int y = (int) (point.getY() * scalingFactor + translateY);
                polygonOut.addPoint(x, y);
            }
            result.add(polygonOut);
        }
        return result;
    }

    private void refreshRectangle() {
        originalRectangles = new ArrayList<>();
        for (Polygon2D polygon : originalPolygons) {
            final Rectangle2D r = polygon.getBounds2D();
            System.out.println("originalRectangle = " + r);
            originalRectangles.add(r);
        }
        this.transformedRectangles = getScaledRectangle(originalRectangles);
    }

    private List<Rectangle2D> getScaledRectangle(List<Rectangle2D> rs) {
        List<Rectangle2D> scaledRectangles = new ArrayList<>();
        for (Rectangle2D r : rs) {
            double x = r.getX() * scalingFactor + translateX;
            double y = r.getY() * scalingFactor + translateY;
            double w = r.getWidth() * scalingFactor;
            double h = r.getHeight() * scalingFactor;
            final Rectangle2D.Double sr = new Rectangle2D.Double(x, y, w, h);
            System.out.println("rect = " + sr);
            scaledRectangles.add(sr);
        }
        return scaledRectangles;
    }

    public void autoScaleAndTranslate() {
        final double width = this.getWidth() - 10;
        final double height = this.getHeight() - 10;
        final Rectangle2D bounds = GeomUtils.getBoundForPolygons(originalPolygons);
        logger.info("Boundary for all polygons: {}", bounds);
        double rw = width / bounds.getWidth();
        double rh = height / bounds.getHeight();
        scalingFactor = Math.min(rh, rw);
        translateX = -((bounds.getX() * scalingFactor) - 10);
        translateY = -((bounds.getY() * scalingFactor) - 10);
        System.out.println("scalingFactor: " + scalingFactor);
        System.out.println("translateX: " + translateX);
        System.out.println("translateY: " + translateY);
        refreshRectangle();
        refreshPolygon();
        refreshPoints();
    }

    public void addPoint(Point2D point2D) {
        this.points.add(point2D);
        refreshPoints();
    }

    public Point2D transformPoint(Point2D p) {
        double x = p.getX() * scalingFactor + translateX;
        double y = p.getY() * scalingFactor + translateY;
        return new Point2D.Double(x, y);
    }

    private void refreshPoints() {
        transformedPoints.clear();
        System.out.println("+++++++++++++++++++++++++++++++++++++++");
        for (int i = 0; i < points.size(); i++) {
            final Point2D p = transformPoint(points.get(i));
            System.out.println(points.get(i) + " )> " + FormattingUtils.format(p));
            transformedPoints.add(p);
        }
        this.repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;

        if (transformedPolygons != null) {
            g2d.setColor(Color.RED);
            for (Polygon polygon : transformedPolygons) {
                g2d.drawPolygon(polygon);
            }
            g2d.setColor(Color.GREEN.brighter());
            for (Polygon polygon : transformedPolygonsReduced) {
                g2d.drawPolygon(polygon);
            }
        }
        if (transformedRectangles != null) {
            final Font font = g2d.getFont();
            g2d.setFont(font.deriveFont(9f));
            for (int i = 0; i < transformedRectangles.size(); i++) {
                g2d.setColor(Color.BLUE);
                final Rectangle2D r = originalRectangles.get(i);
                final Rectangle2D tr = transformedRectangles.get(i);
                g2d.drawRect((int) tr.getX(), (int) tr.getY(), (int) tr.getWidth(), (int) tr.getHeight());
                if (showCoordinates) {
                    g2d.setColor(Color.BLUE.darker());
                    g2d.drawString(r.getX() + ", " + r.getY(), (int) tr.getX(), (int) tr.getY());
                    g2d.drawString((r.getX() + r.getWidth()) + ", " + (r.getY() + r.getHeight()), (int) (tr.getX() + tr.getWidth()), (int) (tr.getY() + tr.getHeight()));
                }
            }
        }
        for (int i = 0; i < points.size(); i++) {
            Point2D tp = transformedPoints.get(i);
            Point2D p = points.get(i);
            g2d.setColor(Color.ORANGE);
            g2d.fillOval((int) tp.getX(), (int) tp.getY(), 3, 3);
            g2d.setColor(Color.LIGHT_GRAY);
            g2d.drawString(p.getX() + ", " + p.getY(), (int) tp.getX(), (int) tp.getY());
        }
    }

    public void clear() {
        transformedPolygons = null;
        transformedRectangles = null;
    }

}
